package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.context.BaseContext;
import com.heima.article.dtos.ApArticleDto;
import com.heima.article.dtos.ApArticleInfoDto;
import com.heima.article.dtos.CollectionBehaviorDto;
import com.heima.article.mapper.*;
import com.heima.article.pojos.*;
import com.heima.article.service.ApArticleContentService;
import com.heima.article.service.ApArticleService;
import com.heima.article.vos.ArticleInfoVo;
import com.heima.behavior.BehaviorDto;
import com.heima.behavior.ReadBehaviorDto;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.common.constants.UserConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.feign.user.UserFeignClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.UpdateArticleMess;
import com.heima.search.vos.SearchArticleVo;
import com.heima.user.pojos.ApUserFollow;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author 郭文杰
 * @description 针对表【ap_article(文章信息表，存储已发布的文章)】的数据库操作Service实现
 * @createDate 2023-12-02 11:42:51
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle>
        implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ApArticleContentService apArticleContentService;

    @Autowired
    private ApCollectionMapper apCollectionMapper;

    @Autowired
    private ApArticlePreferenceMapper apArticlePreferenceMapper;

    @Autowired
    private ApAuthorMapper apAuthorMapper;

    @Autowired
    private ApArticleLikeMapper apArticleLikeMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    //freemarker获取模板
    @Autowired
    private Configuration configuration;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    private CacheService cacheService;


    /**
     * 加载文章
     *
     * @param articleHomeDto 前端dto
     * @param type           1加载最新 2加载更多 3首页
     * @return 结果
     */
    @Override
    public List<ApArticle> loadArticle(ArticleHomeDto articleHomeDto, String type) {
        //查询频道
        String tag = articleHomeDto.getTag();

        //查询条数
        Integer size = articleHomeDto.getSize();
        if (size == null || size < 0 || size > 100) {
            size = 10;
        }

        articleHomeDto.setTag(tag);
        articleHomeDto.setSize(size);
        //加载最新 查询条件
        if ("1".equals(type)) {
            articleHomeDto.setMinBehotTime(null);
        } else if ("2".equals(type)) {
            //加载更多 查询条件
            articleHomeDto.setMaxBehotTime(null);
        } else if ("3".equals(type)) {
            //加载首页 查询条件
            articleHomeDto.setMinBehotTime(null);
            articleHomeDto.setMaxBehotTime(null);
        }
        //查询不喜欢的文章id
        List<Long> articleIds = getUnLikeArticleId(BaseContext.getCurrentId());
        List<ApArticle> list = apArticleMapper.selectArticle(articleHomeDto, articleIds);
        return list;
    }

    /**
     * 获取用户不喜欢的文章列表id
     *
     * @param userId 用户id
     * @return 用户不喜欢的一些文章id集合
     */
    private List<Long> getUnLikeArticleId(Long userId) {
        List<ApArticlePreference> apArticlePreferenceList = getUnLikeArticle(userId);
        if (apArticlePreferenceList == null || apArticlePreferenceList.size() == 0) {
            return null;
        }
        List<Long> articleIds =
                apArticlePreferenceList
                        .stream()
                        .map(ApArticlePreference::getArticleId).collect(Collectors.toList());
        return articleIds;
    }

    /**
     * 获取用户不喜欢的文章列表
     *
     * @param userId 用户id
     * @return 不喜欢的文章列表
     */
    private List<ApArticlePreference> getUnLikeArticle(Long userId) {
        LambdaQueryWrapper<ApArticlePreference> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApArticlePreference::getUserId, userId)
                .eq(ApArticlePreference::getIsIgnored, ArticleConstants.ARTICLE_UNLIKE_WEIGHT);
        return apArticlePreferenceMapper.selectList(wrapper);
    }

    /**
     * 加载每个频道的热点文章数据
     *
     * @param articleHomeDto 条件参数
     * @param type           加载文章类型 1加载最新 2加载更多 3加载首页
     * @return 文章数据
     */
    @Override
    public List<ApArticle> loadArticleByDbOrRedis(ArticleHomeDto articleHomeDto, String type) {
        //redis或数据库中加载数据
        String key = ArticleConstants.HOT_ARTICLE_FIRST_PAGE + articleHomeDto.getTag();
        String articles = cacheService.get(key);
        if (articles != null) {
            //redis中有数据,从redis获取
            return JSON.parseArray(articles, ApArticle.class);
        } else {
            //redis中没有数据,从数据库获取
            return this.loadArticle(articleHomeDto, type);
        }
    }

    /**
     * 保存或修改文章
     *
     * @param apArticleDto 文章信息Dto
     * @return 返回的文章id
     */
    @Override
    public long saveOrUpdateArticle(ApArticleDto apArticleDto) {
        //非空判断
        if (apArticleDto == null ||
                StringUtils.isEmpty(apArticleDto.getContent()) ||
                StringUtils.isEmpty(apArticleDto.getTitle())) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //保存或修改的文章
        ApArticle apArticle = new ApArticle();
        //获取app的userId
        LambdaQueryWrapper<ApAuthor> authorWrapper = new LambdaQueryWrapper<>();
        authorWrapper.eq(ApAuthor::getName, apArticleDto.getAuthorName());
        ApAuthor apAuthor = apAuthorMapper.selectOne(authorWrapper);
        apArticle.setAuthorId(apAuthor.getId());
        BeanUtils.copyProperties(apArticleDto, apArticle);

        //是否存在文章id
        Long id = apArticleDto.getId();
        if (id == null) {
            //保存文章
            this.save(apArticle);
            //保存文章配置
            saveArticleConfig(apArticle);
            //保存文章内容
            saveArticleContent(apArticleDto, apArticle);
        } else {
            //修改文章
            apArticleMapper.updateById(apArticle);
            //保存修改后的文章内容
            LambdaUpdateWrapper<ApArticleContent> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(ApArticleContent::getArticleId, apArticle.getId());
            updateWrapper.set(ApArticleContent::getContent, apArticleDto.getContent());
            apArticleContentService.update(updateWrapper);
        }

        //--------------文章静态化-------------
        articleDetailStatic(apArticleDto, apArticle);

        //--------------同步发布文章article至ES中-------------
        //Kafka发消息
        SearchArticleVo searchArticleVo = new SearchArticleVo();
        BeanUtils.copyProperties(apArticleDto, searchArticleVo);
        searchArticleVo.setContent(apArticleDto.getContent());
        searchArticleVo.setId(apArticle.getId());
        kafkaTemplate.send(ArticleConstants.ARTICLE_ES_SYNC_TOPIC, JSON.toJSONString(searchArticleVo));

        //返回 发布成功到app的文章id
        return apArticle.getId();
    }

    /**
     * 删除app端文章
     *
     * @param id 文章id
     */
    @Override
    public void delArticle(Long id) {
        if (id == null) {
            throw new CustomException(AppHttpCodeEnum.ARTICLE_ID_INVALID);
        }
        //判断文章是否存在
        ApArticle apArticle = apArticleMapper.selectById(id);
        //不存在,抛出异常
        if (apArticle == null) {
            throw new CustomException(AppHttpCodeEnum.ARTICLE_NOT_EXIST);
        }
        //存在,删除文章信息 文章配置 文章内容 文章收藏
        apArticleMapper.deleteById(id);
        LambdaQueryWrapper<ApArticleConfig> configWrapper = new LambdaQueryWrapper<>();
        configWrapper.eq(ApArticleConfig::getArticleId, id);
        apArticleConfigMapper.delete(configWrapper);
        LambdaQueryWrapper<ApArticleContent> contentWrapper = new LambdaQueryWrapper<>();
        contentWrapper.eq(ApArticleContent::getArticleId, id);
        apArticleContentMapper.delete(contentWrapper);
        //查询文章是否收藏,再决定是否删除
        ApCollection apCollection = apCollectionMapper.selectById(id);
        LambdaQueryWrapper<ApCollection> collectionWrapper = new LambdaQueryWrapper<>();
        collectionWrapper.eq(apCollection != null, ApCollection::getArticleId, id);
        apCollectionMapper.delete(collectionWrapper);
    }

    /**
     * 上下架app端文章
     *
     * @param articleId 文章id
     * @param enable    是否上下架 1下架 0上架
     */
    @Override
    public void downOrUp(@RequestParam("articleId") Long articleId, @RequestParam("enable") Short enable) {
        //参数校验
        if (articleId == null || enable == null) {
            return;
        }
        //判断文章配置是否存在
        LambdaQueryWrapper<ApArticleConfig> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(ApArticleConfig::getArticleId, articleId);
        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(wrapper1);
        if (apArticleConfig == null) {
            return;
        }
        LambdaUpdateWrapper<ApArticleConfig> wrapper2 = new LambdaUpdateWrapper<>();
        wrapper2.eq(ApArticleConfig::getArticleId, articleId);
        wrapper2.set(ApArticleConfig::getIsDown, enable.intValue());
        apArticleConfigMapper.update(apArticleConfig, wrapper2);//更新app端文章下架状态
        //删除app端用户收藏的当前文章
        // 当用收藏到已下架的文章,直接删除改文章,否则不做处理
        if (enable.intValue() == 0) {
            LambdaQueryWrapper<ApCollection> wrapper3 = new LambdaQueryWrapper<>();
            wrapper3.eq(ApCollection::getArticleId, articleId);
            ApCollection collection = apCollectionMapper.selectOne(wrapper3);
            if (collection != null) {
                apCollectionMapper.delete(wrapper3);
            }
        }

    }

    /**
     * 保存文章内容
     *
     * @param apArticleDto 文章信息Dto
     * @param apArticle    文章信息
     */
    private void saveArticleContent(ApArticleDto apArticleDto, ApArticle apArticle) {
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(apArticleDto.getContent());
        apArticleContentMapper.insert(apArticleContent);
    }

    /**
     * 保存文章配置
     *
     * @param apArticle 文章信息
     */
    private void saveArticleConfig(ApArticle apArticle) {
        ApArticleConfig articleConfig = new ApArticleConfig();
        articleConfig.setArticleId(apArticle.getId());
        articleConfig.setIsComment(1);
        articleConfig.setIsForward(1);
        articleConfig.setIsDown(0);
        articleConfig.setIsDelete(0);
        apArticleConfigMapper.insert(articleConfig);
    }

    /**
     * 文章详情静态化
     *
     * @param apArticleDto 文章信息dto
     * @param apArticle    文章信息
     */
    private void articleDetailStatic(ApArticleDto apArticleDto, ApArticle apArticle) {
        try {
            //1 freemarker的模板对象，获取模板
            Template template = configuration.getTemplate("article.ftl");

            //解析json,将每一个map类型的jsonObject存入map集合中
            List<Map> contentList = JSON.parseArray(apArticleDto.getContent(), Map.class);

            //2 存放模板中的数据
            Map params = new HashMap();
            params.put("content", contentList);

            //3 合成模板数据 先将生成模板转成字符型=>再获取字符流输入流=>最后通过字符输入流,存入到minio中
            StringWriter stringWriter = new StringWriter();
            //第一个参数 数据模型
            //第二个参数  输出流
            template.process(params, stringWriter);


            //将字符型模板上传到minio中
            InputStream inputstream = new ByteArrayInputStream(stringWriter.toString().getBytes());
            //prefix文件前缀,生成文件的位置
            String contentURL = fileStorageService.uploadHtmlFile("", apArticle.getId() + ".html", inputstream);

            //4 为文章设置 文章详情静态页面url
            apArticle.setStaticUrl(contentURL);
            apArticleMapper.updateById(apArticle);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * <h2>文章点赞</h2>
     * 点赞流程: 1.无记录=>点赞=>插入记录=>设置状态为点赞 2.一定有记录=>点赞=>修改记录=>修改状态为点赞
     * 取消点赞流程: 1.一定有记录=>取消点赞=>修改状态为取消点赞
     *
     * @param behaviorDto 前端dto
     */
    @Override
    public void likeBehavior(BehaviorDto behaviorDto, Long userId) {
        //参数校验
        if (behaviorDto == null || behaviorDto.getArticleId() == null) {
            return;
        }
        //判断文章是否存在
        ApArticle apArticle = apArticleMapper.selectById(behaviorDto.getArticleId());
        if (apArticle == null) {
            return;
        }
        //更新数据库点赞数量
        // updDbLikes(behaviorDto, userId, apArticle);

        //更新redis key设置hash类型: key:前缀_文章id filed:用户id value:随便
        String key = ArticleConstants.ARTICLE_LIKE_BEHAVIOR + "_" + behaviorDto.getArticleId();
        String filed = userId.toString();
        Object value = cacheService.hGet(key, filed);

        //发消息给kafkaStream处理
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setType(UpdateArticleMess.UpdateArticleType.LIKES);
        mess.setArticleId(behaviorDto.getArticleId());

        if (behaviorDto.getOperation() ==
                ArticleConstants.ARTICLE_LIKE_OPERATION_WEIGHT.shortValue()) {
            if (value == null) {
                cacheService.hPut(key, filed, behaviorDto.toString());
                mess.setAdd(1);
            }
        } else if (behaviorDto.getOperation() ==
                ArticleConstants.ARTICLE_CANCEL_LIKE_OPERATION_WIGHT.shortValue()) {
            cacheService.hDelete(key, filed);
            mess.setAdd(-1);
        }
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
    }

    /**
     * 更新数据库点赞数量
     *
     * @param behaviorDto
     * @param userId
     * @param apArticle
     */
    private void updDbLikes(BehaviorDto behaviorDto, Long userId, ApArticle apArticle) {
        //查询用户点赞文章
        ApArticleLike apArticleLike = getArticleLike(behaviorDto.getArticleId(), userId);
        int likes = 0;
        if (behaviorDto.getOperation() ==
                ArticleConstants.ARTICLE_LIKE_OPERATION_WEIGHT.shortValue()) {
            //点赞后:文章点赞数量+1
            likes = apArticle.getLikes() == null ? 1 : apArticle.getLikes() + 1;
            //记录用户点赞文章
            saveArticleLike(behaviorDto.getArticleId(), userId, apArticleLike);
        } else if (behaviorDto.getOperation() ==
                ArticleConstants.ARTICLE_CANCEL_LIKE_OPERATION_WIGHT.shortValue()) {
            //取消点赞后:文章点赞数量-1
            likes = apArticle.getLikes() <= 0 ? 0 : apArticle.getLikes() - 1;
            //记录用户取消点赞文章
            updateArticleLike(apArticleLike, ArticleConstants.ARTICLE_CANCEL_LIKE_OPERATION_WIGHT);
        }
        apArticle.setLikes(likes);
        apArticleMapper.updateById(apArticle);
    }

    /**
     * 根据用户id和文章id,查询用户是否点赞了这篇文章
     *
     * @param articleId 文章id
     * @param userId    用户id
     * @return 返回用户点赞的文章
     */
    private ApArticleLike getArticleLike(Long articleId, Long userId) {
        LambdaQueryWrapper<ApArticleLike> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApArticleLike::getUserId, userId)
                .eq(ApArticleLike::getArticleId, articleId);
        ApArticleLike apArticleLike = apArticleLikeMapper.selectOne(wrapper);
        return apArticleLike;
    }

    /**
     * 修改文章点赞
     *
     * @param apArticleLike 修改点赞的文章信息
     */
    private void updateArticleLike(ApArticleLike apArticleLike, Integer operation) {
        //修改取消点赞状态
        apArticleLike.setOperation(operation.shortValue());
        apArticleLike.setUpdateTime(new Date());
        apArticleLikeMapper.updateById(apArticleLike);
    }

    /**
     * 保存文章点赞
     *
     * @param articleId     文章id
     * @param userId        点赞的用户
     * @param apArticleLike 点赞的文章信息
     */
    private void saveArticleLike(Long articleId, Long userId, ApArticleLike apArticleLike) {
        if (apArticleLike == null) {
            //第一次点赞插入记录
            apArticleLike = new ApArticleLike();
            apArticleLike.setArticleId(articleId);
            apArticleLike.setUserId(userId.shortValue());
            apArticleLike.setType(ArticleConstants.ARTICLE_LIKE_TYPE.shortValue());
            apArticleLike.setOperation(ArticleConstants.ARTICLE_LIKE_OPERATION_WEIGHT.shortValue());
            apArticleLike.setCreateTime(new Date());
            apArticleLike.setUpdateTime(new Date());
            apArticleLikeMapper.insert(apArticleLike);
        } else {
            //第二次点修改点赞状态
            apArticleLike.setCreateTime(new Date());
            updateArticleLike(apArticleLike, ArticleConstants.ARTICLE_LIKE_OPERATION_WEIGHT);
        }
    }

    /**
     * <h2>文章阅读</h2>
     *
     * @param readBehaviorDto 前端dto
     */
    @Override
    public void readBehavior(ReadBehaviorDto readBehaviorDto, Long userId) {
        //参数校验
        if (readBehaviorDto == null ||
                readBehaviorDto.getArticleId() == null ||
                readBehaviorDto.getCount() == null) {
            return;
        }
        Long articleId = readBehaviorDto.getArticleId();
        Integer count = readBehaviorDto.getCount();
        //更新数据库阅读数量
        //updDbViews(readBehaviorDto);

        //更新redis key设计hash类型: key:前缀_文章id field:用户id value:该用户阅读次数
        String key = ArticleConstants.ARTICLE_READ_BEHAVIOR + "_" + articleId;
        String field = userId.toString();

        if (cacheService.hGet(key, field) == null) {
            cacheService.hPut(key, field, count.toString());
        } else {
            Integer views = Integer.valueOf(cacheService.hGet(key, field) + "");
            cacheService.hPut(key, field, (views + count) + "");
        }

        //发消息给kafkaStream处理
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setType(UpdateArticleMess.UpdateArticleType.VIEWS);
        mess.setArticleId(articleId);
        mess.setAdd(1);
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
    }

    /**
     * 更新数据库阅读数量
     *
     * @param readBehaviorDto
     */
    private void updDbViews(ReadBehaviorDto readBehaviorDto) {
        ApArticle apArticle = apArticleMapper.selectById(readBehaviorDto.getArticleId());
        Integer views = apArticle.getViews() == null ?
                readBehaviorDto.getCount() :
                apArticle.getViews() + readBehaviorDto.getCount();
        apArticle.setViews(views);
        apArticleMapper.updateById(apArticle);
    }

    /**
     * <h2>文章不喜欢或不喜欢</h2>
     *
     * @param behaviorDto 前端dto
     */
    @Override
    public void unLikeBehavior(BehaviorDto behaviorDto, Long userId) {
        //参数校验
        if (behaviorDto == null ||
                behaviorDto.getArticleId() == null ||
                behaviorDto.getType() == null) {
            return;
        }
        Long articleId = behaviorDto.getArticleId();
        Short type = behaviorDto.getType();
        //更新数据库文章状态
        updDbUnLike(behaviorDto, userId);
        // 更新redis key设置hash: key:前缀_文章id field用户id value:随便(只要field存在就是不喜欢反之取消不喜欢)
        String key = ArticleConstants.UN_LIKE_BEHAVIOR + "_" + articleId;
        String filed = userId.toString();
        if (type == ArticleConstants.ARTICLE_UNLIKE_WEIGHT.shortValue()) {
            if (cacheService.hGet(key, filed) == null) {
                cacheService.hPut(key, filed, behaviorDto.toString());
            }
        } else {
            cacheService.hDelete(key, filed);
        }
    }

    //更新数据库文章状态
    private void updDbUnLike(BehaviorDto behaviorDto, Long userId) {
        ApArticlePreference articlePreference = getArticlePreference(userId.intValue(), behaviorDto.getArticleId());
        //插入记录或修改记录到文章偏好表中
        saveOrUpdateApArticlePreference(behaviorDto, userId, articlePreference);
    }

    /**
     * 获取用户对这篇喜欢或不喜欢的文章
     *
     * @param userId    用户id
     * @param articleId 文章id
     * @return 文章信息
     */
    private ApArticlePreference getArticlePreference(Integer userId, Long articleId) {
        LambdaQueryWrapper<ApArticlePreference> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApArticlePreference::getArticleId, articleId)
                .eq(ApArticlePreference::getUserId, userId);
        ApArticlePreference apArticlePreference = apArticlePreferenceMapper.selectOne(wrapper);
        return apArticlePreference;
    }

    /**
     * 保存或修改用户的文章偏好信息
     *
     * @param behaviorDto         前端文章dto信息
     * @param userId              文章偏好用户
     * @param apArticlePreference 文章偏好信息
     */
    private void saveOrUpdateApArticlePreference(BehaviorDto behaviorDto,
                                                 Long userId,
                                                 ApArticlePreference apArticlePreference) {
        // type=0不喜欢文章 type=1取消不喜欢文章 先是点赞然后取消点赞
        if ((behaviorDto.getType() == ArticleConstants.ARTICLE_UNLIKE_WEIGHT.shortValue())
                && apArticlePreference == null) {
            //不喜欢--添加一条记录
            apArticlePreference = new ApArticlePreference();
            apArticlePreference.setArticleId(behaviorDto.getArticleId());
            apArticlePreference.setIsIgnored(ArticleConstants.ARTICLE_UNLIKE_WEIGHT.shortValue());
            apArticlePreference.setUserId(Integer.valueOf(userId + ""));
            apArticlePreferenceMapper.insert(apArticlePreference);
        } else {
            //不喜欢--修改记录
            apArticlePreference.setIsIgnored(ArticleConstants.ARTICLE_UNLIKE_WEIGHT.shortValue());
            apArticlePreferenceMapper.updateById(apArticlePreference);
        }
        if (behaviorDto.getType() ==
                ArticleConstants.ARTICLE_CANCEL_UNLIKE_WEIGHT.shortValue()) {
            //取消不喜欢-修改记录
            apArticlePreference.setIsIgnored(ArticleConstants.ARTICLE_CANCEL_UNLIKE_WEIGHT.shortValue());
            apArticlePreferenceMapper.updateById(apArticlePreference);
        }
    }

    /**
     * <h2>文章收藏或取消</h2>
     *
     * @param collectionBehaviorDto 前端dto
     */
    @Override
    public void collectionBehavior(CollectionBehaviorDto collectionBehaviorDto) {
        //参数校验
        if (collectionBehaviorDto == null ||
                collectionBehaviorDto.getEntryId() == null ||
                collectionBehaviorDto.getType() == null ||
                collectionBehaviorDto.getOperation() == null ||
                collectionBehaviorDto.getPublishedTime() == null
        ) {
            return;
        }
        //判断当前是否是登录状态
        Long userId = BaseContext.getCurrentId();
        Long articleId = collectionBehaviorDto.getEntryId();
        Short operation = collectionBehaviorDto.getOperation();
        if (userId == 0) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //更新数据库的收藏
        // updDbCollection(collectionBehaviorDto, userId);
        //更新redis key设计hash类型 key:业务前缀_userid field:文章id value:随便(主要看field)
        /*LambdaQueryWrapper<ApCollection> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(ApCollection::getEntryId, userId.intValue());
        List<ApCollection> apCollections = apCollectionMapper.selectList(wrapper2);
        if (apCollections != null && apCollections.size() != 0) {
            List<Long> articleIds = apCollections.stream()
                    .map(ApCollection::getArticleId)
                    .collect(Collectors.toList());
            List<ApArticle> apArticles = apArticleMapper.selectBatchIds(articleIds);
            String collectionKey = ArticleConstants.ARTICLE_COLLECTION + "_" + "userid" + "_" + userId;
            cacheService.set(collectionKey, JSON.toJSONString(apArticles));
        }*/
        String key = ArticleConstants.ARTICLE_COLLECTION + "_" + userId;
        String field = articleId.toString();

        //发消息给kafkastream处理
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setType(UpdateArticleMess.UpdateArticleType.COLLECTION);
        mess.setArticleId(articleId);

        if (operation == ArticleConstants.ARTICLE_COLLECTION_WEIGHT.shortValue()) {
            if (cacheService.hGet(key, field) == null) {
                cacheService.hPut(key, field, collectionBehaviorDto.toString());
                mess.setAdd(1);
            }
        } else {
            cacheService.hDelete(key, field);
            mess.setAdd(-1);
        }

        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC,JSON.toJSONString(mess));
    }


    /**
     * 文章收藏或取消
     *
     * @param collectionBehaviorDto 前端dto 0收藏 1取消收藏 收藏的文章id
     * @param userId                用户id
     */
    private void updDbCollection(CollectionBehaviorDto collectionBehaviorDto, Long userId) {
        ApArticle article = this.getById(collectionBehaviorDto.getEntryId());
        ApCollection apCollection = getUserCollection(userId.intValue(), collectionBehaviorDto.getEntryId());
        int collections = 0;
        //收藏文章
        if (apCollection == null &&
                collectionBehaviorDto.getOperation().intValue() == ArticleConstants.ARTICLE_COLLECTION_WEIGHT) {
            //添加记录到收藏表中并设置状态为收藏状态
            apCollection = new ApCollection();
            apCollection.setEntryId(userId.intValue());
            apCollection.setArticleId(collectionBehaviorDto.getEntryId());
            apCollection.setType(collectionBehaviorDto.getType().intValue());
            apCollection.setCollectionTime(new Date());
            apCollection.setPublishedTime(collectionBehaviorDto.getPublishedTime());
            apCollectionMapper.insert(apCollection);
            //收藏量+1
            collections = article.getCollection() == null ? 1 : article.getCollection() + 1;
        }
        //取消收藏文章
        if (apCollection != null &&
                collectionBehaviorDto.getOperation().intValue() == ArticleConstants.ARTICLE_CANCEL_COLLECTION_WEIGHT) {
            LambdaQueryWrapper<ApCollection> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ApCollection::getEntryId, userId)
                    .eq(ApCollection::getArticleId, collectionBehaviorDto.getEntryId());
            apCollectionMapper.delete(wrapper);
            //收藏量-1
            collections = article.getCollection() <= 1 ? 0 : article.getCollection() - 1;
        }
        article.setCollection(collections);
        this.updateById(article);
    }

    /**
     * 获取用户收藏信息
     *
     * @param userId    收藏用户
     * @param articleId 收藏文章
     * @return 收藏信息
     */
    public ApCollection getUserCollection(Integer userId, Long articleId) {
        LambdaQueryWrapper<ApCollection> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApCollection::getEntryId, userId)
                .eq(ApCollection::getArticleId, articleId);
        ApCollection apCollection = apCollectionMapper.selectOne(wrapper);
        return apCollection;
    }


    /**
     * <h2>文章状态回显</h2>
     * 状态回显:当用户对某一篇文章点赞 收藏 关注后,退出后再点击这篇文章把这些状态回显给前端
     *
     * @param apArticleInfoDto 前端dto
     * @return 返回结果
     */
    @Override
    public Map<String, Boolean> loadArticleBehavior(ApArticleInfoDto apArticleInfoDto) {
        /*
         * 参数校验
         */
        if (apArticleInfoDto == null
                || apArticleInfoDto.getArticleId() == null
                || apArticleInfoDto.getAuthorId() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        /*
         * 未登录状态下: userid=0
         * 关注状态:未关注高亮显示 点赞状态:未点赞高亮显示  收藏状态:未收藏高亮显示
         */
        Long userId = BaseContext.getCurrentId();
        Long articleId = apArticleInfoDto.getArticleId();
        apArticleInfoDto.setUserId(userId);
        if (userId == 0L) {
            return getNoLoginBehavior();
        }
        /*
         * 登录状态下:
         * 根据用户对文章的操作,展示关注状态 点赞状态 收藏状态
         */
        /* ArticleInfoVo articleInfoVo = new ArticleInfoVo();
        //1 查询关注状态-查询数据库
        setIsFollowBehavior(apArticleInfoDto, articleInfoVo);
        //2 查询收藏状态-查询数据库
        setIsCollectionBehavior(apArticleInfoDto, userId, articleInfoVo);
        //3 查询点赞状态-查询数据库
        setIsLikeBehavior(apArticleInfoDto, userId, articleInfoVo);
        //4 文章是否不喜欢状态-查询数据库
        setIsUnLikeBehavior(apArticleInfoDto, articleInfoVo);*/
        //1 查询关注状态-查询redis
        HashMap<String, Boolean> resultMap = new HashMap<>();
        Double isFollow = cacheService.zScore(
                UserConstants.USER_FOLLOW_LIST + "_" + userId,
                apArticleInfoDto.getAuthorId().toString());
        if (isFollow != null) {
            resultMap.put("isfollow", true);
        } else {
            resultMap.put("isfollow", false);
        }

        //2 查询收藏状态-查询redis
        Object isCollection = cacheService.hGet(
                ArticleConstants.ARTICLE_COLLECTION + "_" + userId,
                articleId.toString());
        if (isCollection != null) {
            resultMap.put("iscollection", true);
        } else {
            resultMap.put("iscollection", false);
        }

        //3 查询点赞状态-查询数据库-查询redis
        Object isLike = cacheService.hGet(
                ArticleConstants.ARTICLE_LIKE_BEHAVIOR + "_" + articleId,
                userId.toString());
        if (isLike != null) {
            resultMap.put("islike", true);
        } else {
            resultMap.put("islike", false);

        }

        //4 文章是否不喜欢状态-查询redis
        Object isUnLike = cacheService.hGet(
                ArticleConstants.UN_LIKE_BEHAVIOR + "_" + articleId,
                userId.toString());
        if (isUnLike != null) {
            resultMap.put("isunlike", true);
        } else {
            resultMap.put("isunlike", false);
        }

        return resultMap;
    }

    /**
     * 游客登录给前端回显状态
     *
     * @return 回显信息vo
     */
    private Map<String, Boolean> getNoLoginBehavior() {
        Map<String, Boolean> resultMap = new HashMap<>();
        resultMap.put("isfollow", false);
        resultMap.put("islike", false);
        resultMap.put("isunlike", false);
        resultMap.put("iscollection", false);
        return resultMap;
    }

    /**
     * 设置用户不喜欢这篇文章状态
     *
     * @param apArticleInfoDto 前端文章信息dto
     * @param articleInfoVo    回显信息vo
     */
    private void setIsUnLikeBehavior(ApArticleInfoDto apArticleInfoDto, ArticleInfoVo articleInfoVo) {
        int userId = BaseContext.getCurrentId().intValue();
        Long articleId = apArticleInfoDto.getArticleId();
        ApArticlePreference apArticlePreference = getArticlePreference(userId, articleId);
        if (apArticlePreference == null) {
            //没有记录,说明该用户对这篇文章还没有设置为不喜欢
            articleInfoVo.setIsUnLike(false);
        }
        if (apArticlePreference != null &&
                apArticlePreference.getIsIgnored() == ArticleConstants.ARTICLE_UNLIKE_WEIGHT.shortValue()) {
            //有记录,说明该用户对这篇文章由取消不喜欢变为不喜欢
            articleInfoVo.setIsUnLike(true);
        }
        if (apArticlePreference != null &&
                apArticlePreference.getIsIgnored() == ArticleConstants.ARTICLE_CANCEL_UNLIKE_WEIGHT.shortValue()) {
            //有记录,说明该用户对这篇文章由不喜欢变为取消不喜欢
            articleInfoVo.setIsUnLike(false);
        }
    }

    /**
     * 设置文章点赞状态,给前端展示
     *
     * @param apArticleInfoDto 前端文章信息dto
     * @param userId           点赞人用户id
     * @param articleInfoVo    回显信息vo
     */
    private void setIsLikeBehavior(ApArticleInfoDto apArticleInfoDto, Long userId, ArticleInfoVo articleInfoVo) {
        ApArticleLike apArticleLike = getArticleLike(apArticleInfoDto.getArticleId(), userId);
        //未点赞-状态展示:有记录,可能是通过点赞后,又取消点赞了。无记录,可能是压根就没点赞
        if (apArticleLike == null) {
            //无记录,可能是压根就没点赞
            articleInfoVo.setIsLike(false);//未点赞
        }
        if (apArticleLike != null &&
                apArticleLike.getOperation() ==
                        ArticleConstants.ARTICLE_CANCEL_LIKE_OPERATION_WIGHT.shortValue()) {
            //有记录,可能是通过点赞后,又取消点赞了
            articleInfoVo.setIsLike(false);//未点赞
        }
        //已点赞-状态展示:有记录,可能是通过取消点赞后,又点赞了
        if (apArticleLike != null &&
                apArticleLike.getOperation() == ArticleConstants.ARTICLE_LIKE_OPERATION_WEIGHT.shortValue()) {
            articleInfoVo.setIsLike(true);
        }
    }

    /**
     * 设置收藏状态给前端回显
     *
     * @param apArticleInfoDto 前端文章信息dto
     * @param userId           收藏人用户id
     * @param articleInfoVo    回显信息vo
     */
    private void setIsCollectionBehavior(ApArticleInfoDto apArticleInfoDto, Long userId, ArticleInfoVo articleInfoVo) {
        ApCollection apCollection = getUserCollection(userId.intValue(), apArticleInfoDto.getArticleId());
        articleInfoVo.setIsCollection(apCollection != null);
    }

    /**
     * 设置关注状态给前端回显
     *
     * @param apArticleInfoDto 前端文章信息dto
     * @param articleInfoVo    回显信息vo
     */
    private void setIsFollowBehavior(ApArticleInfoDto apArticleInfoDto, ArticleInfoVo articleInfoVo) {
        ResponseResult<ApUserFollow> result = userFeignClient.getUserFollow(apArticleInfoDto);
        ApUserFollow userFollow = result.getData();
        Integer code = result.getCode();
        if (code != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        articleInfoVo.setIsFollow(userFollow != null);
    }
}




